/******************************************************************************
 * * hal.c - realize functions in hal.h
 *
 * *(C) Copyright 2019 Asr International Ltd.
 * * All Rights Reserved
 * ******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include "lvgl/lvgl.h"
#include "hal.h"
#include "lv_drivers/common/utils.h"
#include "lv_drivers/common/backlight.h"
#include "lv_drivers/common/lv_pm.h"
#include "lv_drivers/display/crane_lcdc.h"
#include "lv_drivers/display/scaler.h"
#include "lv_drivers/camera/camera_api.h"
#include "hal_bt.h"

#define DUMMY_HAL_PREFIX_SIZE 8
#define PMIC_UV_IRQ_EN 1
#define LOW_BAT_THD   0

#define TRUE    1
#define FALSE    0
#define NO    0
#define YES    1

#define CHECK_ALARM_ID(x) \
    do { \
        if (x == RTC_ALARM_2) { \
            printf("Warning : alarm 2 will not be used yet, use alarm 1 instead~~ \n"); \
        } \
    } while (0)

static lv_task_t * ind_task = NULL;
static lv_task_t *hal_task __attribute((unused));
static AlarmCallback alarm_callback = NULL;
static CB_FUNC charger_cb_usr;
static int chg_sts_last = 0; /* default status is disconnected */
static uint8_t pm_mutex __attribute((unused));
static lv_audio_msg_t * audio_msg = NULL;
static hal_audio_management_t audio_management;
static AUDIO_CTRL_PRIORITY old_curr_priority = AUDIO_CTRL_PRIORITY_INVALID;
#define MAX_BYTES_TO_READ 1024 * 1024

#if USE_CRANE_CAMERA
static uint8_t camera_on_flag = 0;
extern int cam_hw_version_init(void);
extern int cam_offline_init(void);
extern void mci_camera_register_init(void);
#endif

#if !FAKE_BATTERY
extern BOOL IschargerDebugEn(void);
extern unsigned pm812_get_batt_vol(void);
extern UINT32 pm813_get_bat_vol(void);
extern BOOL PMIC_IS_PM813(void);
extern BOOL PMIC_IS_PM813S(void);
static unsigned char battery_percent = 50;
static TASK_HANDLE *bat_task __attribute((unused)) = NULL;

#if PMIC_UV_IRQ_EN != 0
#define UV_DEBOUNCE_TICK 16384 //500ms = 32768/2
static uint32_t ind_uv_irq_cnt = 0; // use to detect connect short lost
static uint32_t uv_irq_tick_count_start = (uint32_t)(-1); //Save the tick for the first uv irq.
static uint32_t total_uv_irq = 0;
#endif

#endif

#if USE_LV_WLAN != 0
extern void hal_wlan_config(void);
#endif

#define IND_FLAG_CHARGE (1 << 0)
#define IND_FLAG_ALARM (1 << 1)
#define IND_FLAG_UVIRQ (1 << 2)

static uint32_t ind_flag = 0;

#define NFFS_FILE_RECORD_TEMP_PATH "C:/nffs_file_record_temp_path.nv"
static lv_fs_file_t nffs_file;

static void alarm_on_callback(void)
{
    ind_flag |= IND_FLAG_ALARM;
    if (pm_state_flag != -1) {
        uos_set_flag(pm_state_flag, PM_RESUME_ALARM, UOS_FLAG_OR);
    }
}

static void charger_irq_cb(void)
{
    //printf("irq cb\n");
    ind_flag |= IND_FLAG_CHARGE;

#if PMIC_UV_IRQ_EN != 0 && !FAKE_BATTERY
    ind_uv_irq_cnt = 0;
    uv_irq_tick_count_start =  (uint32_t)(-1);
#endif

    if (pm_state_flag != -1) {
        uos_set_flag(pm_state_flag, PM_RESUME_USB, UOS_FLAG_OR);
    }
}

#if PMIC_UV_IRQ_EN != 0 && !FAKE_BATTERY
/* ovp 4.3v. uv irq only comes when usb pull out, no uv irq when usb plug in.
 * So if vbus=1, the uv irq comes more in 100ms, connect may lost, check register 0x5B (fsm) and 0x59(A3 bit3: chg_ok)
 * uv irq only enable after pmic vbus =1, disable it after vbus = 0;
 *
 * uv irq and usb irq register in HISR instead of LISR. Support read time or i2c for PMIC
 * If usb pull out, uv irq(<4.3v) come before soc usb irq(<3.3v),  sometimes 6 uv irqs.
 * uv wakup will not light lcd, so need ignore the debounce uv irqs when usb pull out.
 * So at least 6 continues uv irqs need be ignored or multi continues uv irqs in 500ms need be ignored
 * when judge whether wakeup by uv irq.
 *
 */
static void ovp_irq_cb(void)
{
    ind_flag |= IND_FLAG_UVIRQ;

    total_uv_irq ++;
    //printf("uv=%d\n", total_uv_irq);

    ind_uv_irq_cnt ++;

    /* judge whether wakeup by ovp */
    uint32_t tick = Hal_Timer_Get_Counter();
    //printf("ovpirq, %d, tick =%d\n", ind_uv_irq_cnt, tick);

    if(uv_irq_tick_count_start == (uint32_t)(-1) || tick - uv_irq_tick_count_start > UV_DEBOUNCE_TICK) {
        uv_irq_tick_count_start = Hal_Timer_Get_Counter();
        return;
    }

    if(ind_uv_irq_cnt < 7) {
        return;
    } else if (tick - uv_irq_tick_count_start < UV_DEBOUNCE_TICK) {
        return;
    }

    if (pm_state_flag != -1) {
        uos_set_flag(pm_state_flag, PM_RESUME_OVP, UOS_FLAG_OR);
    }
}

static void charger_pmic_ovp_irq_cb(void)
{
    ovp_irq_cb();
}
#endif

extern  bool watch_get_lcd_status(void);
extern void watch_suspend_by_ui(void);
extern VOID MCI_AudioOpenPath(UINT32 PATH);

/*
 *   If debug, check those registers.
 *   uint8_t var;
 *   NingboRead(NINGBO_BASE_Reg, 0x22, &var);
 *   printf("%s: PMIC[0x22] = 0x%x\n",  __func__, var);
 *   NingboRead(NINGBO_BASE_Reg, 0x23, &var);
 *   printf("%s: PMIC[0x23] = 0x%x\n",  __func__, var);
 *   NingboRead(NINGBO_BASE_Reg, 0x5b,  &var);
 *   printf("%s: PMIC[0x5b] = 0x%x\n",  __func__, var);
 *   NingboRead(NINGBO_BASE_Reg, 0x01, &var);
 *   printf("%s: PMIC[0x01] = 0x%x\n",  __func__, var);
 *   NingboRead(NINGBO_BASE_Reg, 0x26, &var);
 *   printf("%s: PMIC[0x26] = 0x%x\n",  __func__, var);
 *   NingboRead(NINGBO_BASE_Reg, 0x59, &var);
 *   printf("%s: PMIC[0x59] = 0x%x\n",  __func__, var);
*/
static void ind_charger(uint32_t uv_irq_cnt)
{
    HAL_CHG_STATUS ret;
    int chg_sts_curr = pmic_get_chg_status();
    //printf("%s: pmic_get_chg_status = %d [1: connect, 0: disconnect], uv_irq_cnt = %d\n", __func__, chg_sts_curr, uv_irq_cnt);

    if (chg_sts_last != chg_sts_curr) {
#if PMIC_UV_IRQ_EN != 0
        if(chg_sts_curr == 1) {
            Ningbo_INT_ENABLE(NINGBO_VBUS_OVP_INT);
        } else {
            Ningbo_INT_DISABLE(NINGBO_VBUS_OVP_INT);
        }
#endif
        if (charger_cb_usr) {
            ret = (chg_sts_curr == 1)?HAL_CHG_CONNECTED:HAL_CHG_DISCONNECTED;
            charger_cb_usr((void *)ret);
        }
        chg_sts_last = chg_sts_curr;
    }
    else if ((Hal_Powerup_Get_Reason() == HAL_POWERUP_USB || (Hal_Powerup_Get_Reason() == HAL_POWERUP_BAT)) \
            && (chg_sts_curr == 0)) {
#if PMIC_UV_IRQ_EN != 0
        Ningbo_INT_DISABLE(NINGBO_VBUS_OVP_INT);
#endif
        ret = HAL_CHG_DISCONNECTED;
        if (charger_cb_usr) charger_cb_usr((void *)ret);
    }
#if PMIC_UV_IRQ_EN != 0 && !FAKE_BATTERY
    else if ((uv_irq_cnt > 1) && (chg_sts_curr == 1)) {
        if(IschargerDebugEn()) {
            printf("uv irq total =%d\n", total_uv_irq);
            printf("%s: uv_irq_cnt > 1, chg_sts_curr == 1, need do mmpt.\n", __func__);
        }
        //printf("uv irq set mppt\n");
        pmic_set_mppt();
        if(!watch_get_lcd_status()) {
            watch_suspend_by_ui();
        }
    }
#endif
}

typedef struct {
    int user;
    SEMA_ID *sema;
}sd_info_t;

static sd_info_t sd_info;

#define USB_CONNECTED_TO_PC 1

#if USE_LV_SD_FS
/*
 * APP use
 * capture: HAL_sd_user_set(1),
 *          if user<0          return -1, can not write/read sd card
 *          if user>=0         return 0,  can write/read sd card, internal user++
 * release: HAL_sd_user_set(0),
 *                             return 0,  internal user--
 *
 * cb for check usb connect, use:
 * if usb plug in PC: HAL_sd_user_set(-1)
 * if usb pull out from PC: HAL_sd_user_set(-2)
 * if usb pull out already and sd card auto remount OK: HAL_sd_user_set(0)
 */
int HAL_sd_user_set(int val)
{
    sd_info_t *p = &sd_info;

    if((val > 0) && (p->user < 0)) {
        printf("SD card is mounted as U storage, sd card can not be used!\n");
        return -1;
    } else if((0 == val) && (p->user == SD_REQUEST_UNMOUNT_FROM_U_STORAGE)) {
        printf("SD card, recovered, can be used!\n");
    }

    Hal_Sema_Lock(p->sema);
    if(val > 0) {
        p->user++; //sd user capture
    } else if((val == 0) && (p->user > 0)) {
        p->user--; //sd user release
    } else {
        p->user = val;
    }
    Hal_Sema_Unlock(p->sema);
    return 0;
}

int HAL_sd_user_get(void)
{
   sd_info_t *p = &sd_info;

   return p->user;
}
#else
int HAL_sd_user_set(int val)
{
    return 0;
}
int HAL_sd_user_get(void)
{
   return 0;
}

#endif

static void sd_card_recover(void)
{
#if USE_LV_SD_FS
    /* If U storage unmounting, need polling to check sd card whether is ready, then permit app to use. */
    if(SD_REQUEST_UNMOUNT_FROM_U_STORAGE == HAL_sd_user_get()) {
        if(!sdcard_is_ready()) {
            printf("[%s] U storage is unmounted, no sdcard.\n", __func__);
            return;
        }

        if(!sdcard_fat_is_ok()){
            printf("[%s] U storage is unmounted, SD card FAT32 system is not ok\r\n", __func__);
            return;
        }

        HAL_sd_user_set(SD_REMOUNT_OK);
        printf("[%s] U storage is unmounted, SD card remount ok.\r\n", __func__);
        return;
    }
#endif
    return;
}

#if USE_LV_SD_FS
static TASK_HANDLE *u_storage_task = NULL;

static void u_storage_cb(void * para)
{
    /*If usb irq come, check whether need mount as U storage or unmount from U storage */
    int usb_connect_status;

    usb_connect_status = pmic_get_chg_status();
    printf("[%s] usb_connect_status = %d[1: connect, 0: disconnect]\n", __func__, usb_connect_status);

    if((1 == usb_connect_status) && (SD_REMOUNT_OK == HAL_sd_user_get())) {
        if(!sdcard_is_ready()) {
            printf("[%s] no sdcard.\n", __func__);
            goto exit;
        }

        if(!sdcard_fat_is_ok()){
            printf("[%s] SD card FAT32 system is not ok\r\n", __func__);
            goto exit;
        }

        /* charger_is_usb spends 2s to check whether connect to PC host */
        if(charger_is_usb() != USB_CONNECTED_TO_PC) {
            printf("[%s] usb is not connect to PC.\n", __func__);
            goto exit;
        }

        HAL_sd_user_set(SD_MOUNTED_AS_U_STORAGE);
        //enable U storage;
        USBDeviceSelect(USB_STORAGE_ONLY);
    } else if(0 == usb_connect_status) {
        if(SD_MOUNTED_AS_U_STORAGE == HAL_sd_user_get()) {
            HAL_sd_user_set(SD_REQUEST_UNMOUNT_FROM_U_STORAGE);
        }
    }

exit:
    return;
}
#endif

static void indication_task(lv_task_t *task)
{
    if(ind_flag == 0) {
        /* polling to check whether need recover sd card status. */
        sd_card_recover();
        return;
    }

    uint32_t flag;
    unsigned status;

    status = irq_disable();
    flag = ind_flag;
#if PMIC_UV_IRQ_EN != 0 && !FAKE_BATTERY
    uint32_t uv_irq_cnt;
    uv_irq_cnt = ind_uv_irq_cnt;
#endif
    irq_restore(status);

    if (flag & IND_FLAG_CHARGE) {
        ind_flag &= ~IND_FLAG_CHARGE;
        ind_charger(0);
        //usb connect to pc, mount U storage
#if USE_LV_SD_FS
        if (u_storage_task != NULL) {
            uos_delete_task(u_storage_task);
            u_storage_task = NULL;
        }
#if UI_IN_XIP
        static lv_xip_thread param0;
        param0.func = u_storage_cb;
        param0.params = NULL;
        u_storage_task = uos_create_task(
             lv_xip_thread_entry,
             &param0,
             0,
             0x2000,
             240,
             "u_storage_task");
#else
        u_storage_task = uos_create_task(u_storage_cb, NULL, 0, 0x2000, 240, "u_storage_task");
#endif
#endif
    }

#if PMIC_UV_IRQ_EN != 0 && !FAKE_BATTERY
    if (flag & IND_FLAG_UVIRQ) {
        ind_flag &= ~IND_FLAG_UVIRQ;
        ind_uv_irq_cnt = 0;
        uv_irq_tick_count_start =  (uint32_t)(-1);
        ind_charger(uv_irq_cnt);
    }
#endif

    if (flag & IND_FLAG_ALARM) {
        ind_flag &= ~IND_FLAG_ALARM;
        alarm_callback();
    }
}

static void indication_task_init(void)
{
    if(ind_task == NULL)
        ind_task = lv_task_create(indication_task, 100, LV_TASK_PRIO_HIGHEST, NULL);
}

/* HAL RTC interface end */
/**
 * Get RTC time
 * param (out) time:  hal_rtc_t*
 * return  void
 */
VOID Hal_Rtc_Gettime(hal_rtc_t *time)
{
    pmic_rtc_get_time((rtc_time_t *)time);
}

/**
 * Set RTC time
 * param (in) time:  hal_rtc_t*
 * return  void
 */
VOID Hal_Rtc_Settime(hal_rtc_t *time)
{
    pmic_rtc_set_time((rtc_time_t *)time);
}

/**
 * Set alarm
 * param (in) id:  RTC_ALARM_ID
 * param (in) time:  hal_rtc_t*
 * param (in) CallBack: AlarmCallback
 * return  void
 */
VOID Hal_Rtc_Set_Alarm(RTC_ALARM_ID id, hal_rtc_t *time, AlarmCallback CallBack)
{
    CHECK_ALARM_ID(id);
    alarm_callback = CallBack;
    pmic_rtc_set_alarm((rtc_time_t *)time, (alarm_cb_t)alarm_on_callback);
}

/**
 * get Alarm information
 * param (in) id: RTC_UART_ID
 * param (out) time: hal_rtc_t *
 * return  void
 */
VOID Hal_Rtc_Get_Alarm(RTC_ALARM_ID id, hal_rtc_t *time)// get alarm info
{
    CHECK_ALARM_ID(id);
    pmic_rtc_get_alarm((rtc_time_t *)time);
}

/**
 * start/stop alarm
 * param (in) id:  RTC_ALARM_ID
 * param (in) onoff: BOOL
 * return  void
 */
VOID Hal_Rtc_Enable_Alarm(RTC_ALARM_ID id, BOOL onoff)
{
    CHECK_ALARM_ID(id);
    pmic_rtc_enable_alarm(onoff?1:0);
}                                                                                 // start/stop alarm
/* HAL RTC interface end */

/* HAL battery interface begin */
/**
 * get battery status
 * param (in) void:
 * return battery remain percentage
 */
UINT8 Hal_Battery_Get_Status(VOID)
{
#if FAKE_BATTERY
    return pmic_get_bat_remain();
#else
    return battery_percent;
#endif
}

#if !FAKE_BATTERY
typedef struct temp_val_table_s {
    int temp;
    int val;   //mv
} temp_val_table_t;

#define TABLE_TEMP_VAL_LENGTH 68
const temp_val_table_t table_temp_val[TABLE_TEMP_VAL_LENGTH] =
    {{-40, 188},{-39, 178},{-38, 169},{-37, 160},{-36, 152},{-35, 144},{-34, 136},{-33, 130},{-32, 123},{-31, 117},
     {-30, 111},{-29, 105},{-28, 100},{-27, 95},{-26, 90},{-25, 86},{-24, 82},{-23, 78},{-22, 74},{-21, 71},
     {-20, 67},{-19, 64},{-18, 61},{-17, 58},{-16, 56},{-15, 53},{-14, 51},{-13, 48},{-12, 46},{-11, 44},
     {-10, 42},{-9, 40},{-8, 38},{-7, 37},{-6, 35},{-5, 33},{-4, 32},{-3, 31},{-2, 29},{-1, 28},
     {0, 27},{1, 26},{2, 25},{3, 24},{4, 23},{5, 22},{6, 21},{7, 20},{8, 19},{9, 18},
     {11, 17},{12, 16},{14, 15},{16, 14},{18, 13},{20, 12},{22, 11},{25, 10},{27, 9},{31, 8},
     {34, 7},{39, 6},{44, 5},{51, 4},{60, 3},{73, 2},{99, 1},{125, 0}};

static INT32 battery_val_2_temp(UINT32 val)
{
    int i = 0;
    INT32 temp = 0;
    for(i=0; i<TABLE_TEMP_VAL_LENGTH; i++) {
        if(val >= table_temp_val[i].val) {
            temp = table_temp_val[i].temp;
            break;
        }
    }

    return temp;
}
#endif

extern UINT32 pm813s_get_Icharge_meas_cur_mA(void);
VOID Hal_Battery_Get_Values(battery_values_t *values)
{
#if !FAKE_BATTERY
    if(values == NULL){
        printf("Hal_Battery_Get_Values param failed!!\n");
        return;
    }

    memset(values, 0, sizeof(battery_values_t));
    if(PMIC_IS_PM813()||PMIC_IS_PM813S())
    {
        if(Hal_Charger_Get_Status() == HAL_CHG_CONNECTED) {
            values->charge_cur_mA = PMIC_IS_PM813() ? pm813_get_Icharge_meas_cur_mA() : pm813s_get_Icharge_meas_cur_mA();
            values->battery_val_mV = pm813_get_battery_voltage_withoutCharger();
        } else {
            values->charge_cur_mA = 0;
#ifdef USE_WATCH_LITE
            //sdk use SUPPORT_PM803_ONLY to ignore pm813 and others pmic
            values->battery_val_mV = 0;
#else
            values->battery_val_mV = pm813_get_bat_vol();
#endif
        }

        uint32_t temp_val = pm813_get_battemp_meas_vol_mv();
        if(temp_val != 0xFFF) {
            values->battery_status = 1;
            values->battery_temp = battery_val_2_temp(temp_val);
        } else {
            values->battery_status = 0;
        }
    }
    else
    {
        values->battery_status = 0;
        values->charge_cur_mA = 0;
        values->battery_temp = 0;  //no temp adc
        values->battery_val_mV = pm812_get_batt_vol();
    }
    printf("Hal_Battery_Get_Values[%d]: val: %d, cur: %d, temp: %d\n",
        values->battery_status,
        values->battery_val_mV,
        values->charge_cur_mA,
        values->battery_temp);
#endif
}

static CB_FUNC cbLowPower = NULL;

VOID Hal_Register_Low_Power_Ind(CB_FUNC cb)
{
    cbLowPower = cb;
}
/* HAL battery interface end */

/* HAL charger interface begin */
VOID Hal_Register_Charger_Status_Ind(CB_FUNC cb)
{
    charger_cb_usr = cb;
    charger_irq_cb();/* update status */
}

/**
 * get charger status
 * param (in) void:
 * return  HAL_CHG_STATUS
 */
HAL_CHG_STATUS Hal_Charger_Get_Status(VOID)
{
    //printf("%s: chg_sts_last= 0x%x\n",  __func__, chg_sts_last); //0: disconnect; 1: connect
    return((chg_sts_last == 1)?HAL_CHG_CONNECTED:HAL_CHG_DISCONNECTED);
}

/* HAL charger interface end */

/* HAL lcd interface begin */
/**
 * wake up lcd
 * param (in) void:
 * return  void
 */
VOID Hal_Lcd_Wakeup(VOID)
{
    lv_pm_part_resume(LV_PM_PART_DISPLAY);
}

/**
 * make lcd go to sleep to save power
 * param (in) void:
 * return  void
 */
VOID Hal_Lcd_Sleep(VOID)
{
    lv_pm_part_suspend(LV_PM_PART_DISPLAY);
}

/* HAL lcd interface end */

/**
 * wake up touch
 * param (in) void:
 * return  void
 */
VOID Hal_Touch_Wakeup(VOID)
{
    lv_pm_part_resume(LV_PM_PART_TOUCH);
}

/**
 * make touch go to sleep to save power
 * param (in) void:
 * return  void
 */
VOID Hal_Touch_Sleep(VOID)
{
    lv_pm_part_suspend(LV_PM_PART_TOUCH);
}

/**
 * wake up touch and LCD
 * param (in) void:
 * return  void
 */
VOID Hal_Lcd_Touch_Wakeup(VOID)
{
    lv_pm_part_resume(LV_PM_PART_DISPLAY);
    lv_pm_part_resume(LV_PM_PART_TOUCH);
}

/**
 * make touch and LCD go to sleep to save power
 * param (in) void:
 * return  void
 */
VOID Hal_Lcd_Touch_Sleep(VOID)
{
    lv_pm_part_suspend(LV_PM_PART_DISPLAY);
    lv_pm_part_suspend(LV_PM_PART_TOUCH);
}


/**
 * refresh lcd in ambient mode
 * param (in) void:
 * return  void
 */
VOID Hal_Lcd_idle_refresh(VOID)
{
    extern void lcd_idle_refresh(void);
    lcd_idle_refresh();
}


/* HAL backlight interface begin */

/**
 * turn on backlight
 * param (in) void:
 * return  void
 */
VOID Hal_Backlight_On(VOID)
{
    lcd_backlight_on();
}

/**
 * turn off backlight
 * param (in) void:
 * return  void
 */
VOID Hal_Backlight_Off(VOID)
{
    lcd_backlight_off();
}

/**
 * set backlight intensity level
 * param (in) intensity:   HAL_INTENSITY_LEVEL
 * return  void
 */
VOID Hal_Backlight_Intensity_Set(HAL_INTENSITY_LEVEL intensity)
{
    int level = 5;
    switch(intensity) {
        case HAL_INTENSITY_LEVEL_1:
            level = 1;
            break;
        case HAL_INTENSITY_LEVEL_2:
            level = 2;
            break;
        case HAL_INTENSITY_LEVEL_3:
            level = 3;
            break;
        case HAL_INTENSITY_LEVEL_4:
            level = 4;
            break;
        case HAL_INTENSITY_LEVEL_5:
            level = 5;
            break;
        default:
            break;
    }
    lcd_backlight(level);
}

/**
 * set backlight intensity level
 * param (in) intensity:   int level
 * return  void
 */
VOID Hal_Backlight_Intensity_Set_For_Oledpanel(int intensity)
{
    lcd_backlight(intensity);
}

/**
 *get backlight status
 *param (in) void:
 *return UINT8: 0 off, 1 on
 */
UINT8 Hal_Get_Backlight_Status(VOID)
{
    UINT8 onoff;

    lcd_backlight_status(&onoff, NULL);

    return onoff;
}

/* HAL backlight interface end */

typedef enum{
   VIBRATOR_PLAY_REPEAT,
   VIBRATOR_PLAY_ONCE,
} VIBRATOR_TYPE;

typedef enum{
   VIBRATOR_STOP,
   VIBRATOR_PLAYING,
   VIBRATOR_SUSPEND,
} VIBRATOR_STATUS;

/* HAL vibrator interface begin */
typedef struct {
    CB_FUNC cb;
    void * cb_arg;
    uint32_t last_tick;
    uint32_t play_dur;
    uint32_t suspend_dur;
    VIBRATOR_TYPE type;
    VIBRATOR_STATUS status;
}vibrator_info_t;

static vibrator_info_t vibrator_info;

/**
 * vibrate only one time
 * param (in) func: CB_FUNC  call back function which is called when vibrate over.
 * param (in) para: UINT32  play duration, ms
 * return  void
 */
VOID Hal_Vibrator_Play_Onetime(CB_FUNC func, UINT32 para)
{
    vibrator_info_t *p = &vibrator_info;

    if(p->status != VIBRATOR_STOP) {
        Hal_Vibrator_Play_End();
    }

    p->play_dur = para; //ms
    p->suspend_dur = 0;
    p->cb = func;
    p->cb_arg = NULL;
    p->type = VIBRATOR_PLAY_ONCE;
    p->status = VIBRATOR_PLAYING;
    p->last_tick = lv_tick_get();
    pmic_vibrator_on();
}

/**
 * vibrate only one time
 * param (in) func: CB_FUNC  call back function which is called when play over.
 * param (in) para: void *  para of call back function
 * param (in) duration: UINT32  play duration, ms
 * return  void
 */
VOID Hal_Vibrator_Play_Onetime_With_Para(CB_FUNC func, void *para, UINT32 duration)
{
    vibrator_info_t *p = &vibrator_info;

    if(p->status != VIBRATOR_STOP) {
        Hal_Vibrator_Play_End();
    }

    p->play_dur = duration; //ms
    p->suspend_dur = 0;
    p->cb = func;
    p->cb_arg = para;
    p->type = VIBRATOR_PLAY_ONCE;
    p->status = VIBRATOR_PLAYING;
    p->last_tick = lv_tick_get();
    pmic_vibrator_on();
}

/**
 * keep vibrating
 * param (in) void:
 * return  void
 */
VOID Hal_Vibrator_Play_Repeat(VOID)
{
    vibrator_info_t *p = &vibrator_info;

    if(p->status != VIBRATOR_STOP) {
        Hal_Vibrator_Play_End();
    }

    p->play_dur = 1000; //ms
    p->suspend_dur = 1000; //ms
    p->cb = NULL;
    p->type = VIBRATOR_PLAY_REPEAT;
    p->status = VIBRATOR_PLAYING;
    p->last_tick = lv_tick_get();
    pmic_vibrator_on();
}

/**
 * stop vibrating
 * param (in) void:
 * return  void
 */
VOID Hal_Vibrator_Play_End(VOID)
{
    pmic_vibrator_off();
    if(vibrator_info.cb) vibrator_info.cb(vibrator_info.cb_arg);
    memset(&vibrator_info, 0, sizeof(vibrator_info_t));
}

/* HAL vibrator interface end */

/* HAL audio&tone interface begin */

typedef struct {
    SEMA_ID *sema;
    AUDIO_RECORD_CB_FUNC cb;
    uint32_t has_event;
    MCI_EVNET_T event;
    MCI_INFO_T info_type;
    int32_t value;
}record_info_t;
record_info_t record_info;

typedef struct {
    CB_FUNC cb;
    void * cb_arg;
    int finished;
    int closed;
    SEMA_ID *sema;
    lv_fs_file_t fp; /*recorder file*/
}audio_info_t;

static audio_info_t audio_info;

/* audio_play_cb is called in another thread */
static void audio_play_cb(MCI_ERR_T result)
{
    audio_info_t *p = &audio_info;

    Hal_Sema_Lock(p->sema);
    p->finished = 1;
    Hal_Sema_Unlock(p->sema);

    if (result != MCI_ERR_END_OF_FILE) {
        printf("%s error: result = %d\n", __FUNCTION__, result);
    } else {
        if(NULL != p->fp.file_d) {
            printf("%s: p->fp.file_d is not NULL,  p->fp.file_d:%u!\n", __FUNCTION__, (unsigned int)p->fp.file_d);
        } else {
            Hal_Sema_Lock(p->sema);
            if (NULL != audio_msg) {
                lv_mem_free(audio_msg->buffer);
                audio_msg->buffer = NULL;
                lv_mem_free(audio_msg);
                audio_msg = NULL;
            }
            Hal_Sema_Unlock(p->sema);
        }
    }
}

static void hal_task_fn(void *param)
{
    audio_info_t *p = &audio_info;
    int finished;

    Hal_Sema_Lock(p->sema);
    finished = p->finished;
    Hal_Sema_Unlock(p->sema);

    if ((finished == 1) && (p->closed == 0)) {
        if ((p->fp.file_d) && *(int *)p->fp.file_d > 0) {
            Hal_File_Play_End();
        } else {
            Hal_Tone_Play_End();
        }

        if (p->cb) {
            p->cb(p->cb_arg);
        }
    }

    Hal_Sema_Lock(record_info.sema);
    if((record_info.has_event) && (record_info.cb!=NULL)){
        record_info.cb(record_info.event, record_info.info_type, record_info.value);
    }
    record_info.has_event = 0;
    Hal_Sema_Unlock(record_info.sema);

    vibrator_info_t *v = &vibrator_info;

    if(v->status == VIBRATOR_PLAYING && v->type == VIBRATOR_PLAY_ONCE) {
        if(tick_to_ms(lv_tick_elaps(v->last_tick)) > v->play_dur) {
            Hal_Vibrator_Play_End();
        }
    } else if(v->type == VIBRATOR_PLAY_REPEAT) {
        uint32_t durMs = tick_to_ms(lv_tick_elaps(v->last_tick));
        if(v->status == VIBRATOR_PLAYING) {
            if(durMs > v->play_dur) {
                pmic_vibrator_off();
                v->status = VIBRATOR_SUSPEND;
                v->last_tick = lv_tick_get();
            }
        } else if(v->status == VIBRATOR_SUSPEND){
            if(durMs > v->suspend_dur) {
                pmic_vibrator_on();
                v->status = VIBRATOR_PLAYING;
                v->last_tick = lv_tick_get();
            }
        }
    }
}

static int to_mci_volume(int vol)
{
    int ret;

    if (vol <= 0) {
        ret = 0;
    } else if (vol >= MCI_SPK_MAX) {
        ret = MCI_SPK_MAX;
    } else {
        ret = (vol * MCI_SPK_MAX) / HAL_AUDIO_SPK_LEVEL_MAX;
    }

    return ret;
}

/**
 * play tone for one time
 * param (in) buffer:  UINT32*
 * param (in) len:  UINT32
 * param (in) volume:  HAL_SPEAKER_GAIN
 * param (in) func: CB_FUNC  call back function which is called when play over.
 * param (in) para: UINT32  para of call back function
 * return  void
 */
VOID Hal_Tone_Play_Onetime(UINT32 *buffer, UINT32 len, HAL_SPEAKER_GAIN volume, CB_FUNC func,
                           void *para)
{
    audio_info_t *p = &audio_info;
    int32_t ret = 0;

    if ((buffer == NULL) || (len == 0)) {
        printf("%s: invalid parameter.\n", __FUNCTION__);
        return ;
    }

    if (p->closed == 0) {
        ret = MCI_AudioStopBuffer();
        if (ret != MCI_ERR_NO) {
            printf("%s: MCI_AudioStopBuffer failed,  ret:%d!\n", __FUNCTION__, ret);
        }
        p->closed = 1;
    }

    Hal_Sema_Lock(p->sema);
    p->finished = 0;
    Hal_Sema_Unlock(p->sema);

    MCI_AudioOpenPath(MCI_PATH_LSP);
    MCI_AudioSetVolume(to_mci_volume(volume));
    p->cb = func;
    p->cb_arg = para;
    ret = MCI_AudioPlayBuffer((INT32 *)buffer, len, 0, audio_play_cb, (MCI_PLAY_MODE_T)0, 0);
    if (ret != MCI_ERR_NO) {
        printf("%s: MCI_AudioPlayBuffer failed, ret:%d!\n", __FUNCTION__, ret);
    }
    p->closed = 0;
}

//for playing media tone, if bt is connected,play with bt.
VOID Hal_Media_Tone_Play_Onetime(UINT32 *buffer, UINT32 len, HAL_SPEAKER_GAIN volume, CB_FUNC func, void *para)
{
    audio_info_t *p = &audio_info;
    int32_t ret = 0;

    if ((buffer == NULL) || (len == 0)) {
        printf("%s: invalid parameter.\n", __FUNCTION__);
        return ;
    }

    if (p->closed == 0) {
        ret = MCI_AudioStopBuffer();
        if (ret != MCI_ERR_NO) {
            printf("%s: MCI_AudioStopBuffer failed,  ret:%d!\n", __FUNCTION__, ret);
        }
        p->closed = 1;
    }

    Hal_Sema_Lock(p->sema);
    p->finished = 0;
    Hal_Sema_Unlock(p->sema);

    MCI_AudioSetVolume(to_mci_volume(volume));
    p->cb = func;
    p->cb_arg = para;
#if USE_LV_BLUETOOTH != 0
    extern INT32 MCI_AudioPlayBufferWithBtConnected(INT32 * pBuffer, UINT32 len, UINT8 loop, MCI_AUDIO_BUFFER_PLAY_CALLBACK_T callback,
                                                    MCI_PLAY_MODE_T format, INT32 startPosition);


    if(bt_get_headset_connect()) {
        MCI_AudioOpenPath(MCI_PATH_BT);
        MCI_init_btVolLevel_table(MCI_SPK_MAX, 11);
        MCI_AudioSetVolume_Bt(to_mci_volume(volume));
        ret = MCI_AudioPlayBufferWithBtConnected((INT32 *)buffer, len, 0, audio_play_cb, (MCI_PLAY_MODE_T)0, 0);
    } else
#endif
    {
        MCI_AudioOpenPath(MCI_PATH_LSP);
        MCI_AudioSetVolume(to_mci_volume(volume));
        ret = MCI_AudioPlayBuffer((INT32 *)buffer, len, 0, audio_play_cb, (MCI_PLAY_MODE_T)0, 0);
    }
    if (ret != MCI_ERR_NO) {
        printf("%s: MCI_AudioPlayBuffer failed, ret:%d!\n", __FUNCTION__, ret);
    }
    p->closed = 0;
}

/**
 * play tone repeatedly
 * param (in) buffer:  UINT32*
 * param (in) len:  UINT32
 * param (in) volume:  HAL_SPEAKER_GAIN
 * return  void
 */
VOID Hal_Tone_Play_Repeat(UINT32 *buffer, UINT32 len, HAL_SPEAKER_GAIN volume)
{
    int32_t ret = 0;
    audio_info_t *p = &audio_info;

    if ((buffer == NULL) || (len == 0)) {
       printf("%s: invalid parameter.\n", __FUNCTION__);
       return ;
    }

    if (p->closed == 0) {
        ret = MCI_AudioStopBuffer();
        if (ret != MCI_ERR_NO) {
            printf("%s: MCI_AudioStopBuffer failed, ret:%d!\n", __FUNCTION__, ret);
        }
        p->closed = 1;
    }

    Hal_Sema_Lock(p->sema);
    p->finished = 0;
    Hal_Sema_Unlock(p->sema);

    MCI_AudioOpenPath(MCI_PATH_LSP);
    MCI_AudioSetVolume(to_mci_volume(volume));
    p->cb = NULL;
    MCI_AudioPlayBuffer((INT32 *)buffer, len, 1, audio_play_cb, (MCI_PLAY_MODE_T)0, 0);
    if (ret != MCI_ERR_NO) {
        printf("%s: MCI_AudioPlayBuffer failed, ret:%d!\n", __FUNCTION__, ret);
    }
    p->closed = 0;
}

//for playing media tone, if bt is connected,play with bt.
VOID Hal_Media_Tone_Play_Repeat(UINT32 *buffer, UINT32 len, HAL_SPEAKER_GAIN volume)
{
    int32_t ret = 0;
    audio_info_t *p = &audio_info;

    if ((buffer == NULL) || (len == 0)) {
       printf("%s: invalid parameter.\n", __FUNCTION__);
       return ;
    }

    if (p->closed == 0) {
        ret = MCI_AudioStopBuffer();
        if (ret != MCI_ERR_NO) {
            printf("%s: MCI_AudioStopBuffer failed, ret:%d!\n", __FUNCTION__, ret);
        }
        p->closed = 1;
    }

    Hal_Sema_Lock(p->sema);
    p->finished = 0;
    Hal_Sema_Unlock(p->sema);

    MCI_AudioSetVolume(to_mci_volume(volume));
    p->cb = NULL;
    #if USE_LV_BLUETOOTH != 0
    extern INT32 MCI_AudioPlayBufferWithBtConnected(INT32 * pBuffer, UINT32 len, UINT8 loop, MCI_AUDIO_BUFFER_PLAY_CALLBACK_T callback,
                                                    MCI_PLAY_MODE_T format, INT32 startPosition);


    if(bt_get_headset_connect()) {
        MCI_AudioOpenPath(MCI_PATH_BT);
        MCI_init_btVolLevel_table(MCI_SPK_MAX, 11);
        MCI_AudioSetVolume_Bt(to_mci_volume(volume));
        ret = MCI_AudioPlayBufferWithBtConnected((INT32 *)buffer, len, 1, audio_play_cb, (MCI_PLAY_MODE_T)0, 0);
    } else
#endif
    {
        MCI_AudioOpenPath(MCI_PATH_LSP);
        MCI_AudioSetVolume(to_mci_volume(volume));
        ret = MCI_AudioPlayBuffer((INT32 *)buffer, len, 1, audio_play_cb, (MCI_PLAY_MODE_T)0, 0);
    }
    if (ret != MCI_ERR_NO) {
        printf("%s: MCI_AudioPlayBuffer failed, ret:%d!\n", __FUNCTION__, ret);
    }
    p->closed = 0;
}

/**
 * stop playing tone
 * param (in) void:
 * return  void
 */
VOID Hal_Tone_Play_End(VOID)
{
    audio_info_t *p = &audio_info;
    int32_t ret = 0;

    ret = MCI_AudioStopBuffer();
    if (ret != MCI_ERR_NO) {
        printf("%s: MCI_AudioStopBuffer failed,  ret:%d!\n", __FUNCTION__, ret);
    }
    p->closed = 1;
    Hal_Sema_Lock(p->sema);
    p->finished = 1;

    if (NULL != audio_msg) {
        lv_mem_free(audio_msg->buffer);
        audio_msg->buffer = NULL;
        lv_mem_free(audio_msg);
        audio_msg = NULL;
    }
    Hal_Sema_Unlock(p->sema);
}

/**
 * play file not in flash fs for one time
 * param (in) path: const char*
 * param (in) volume: HAL_SPEAKER_GAIN
 * param (in) func: CB_FUNC  call back function which is called when play over.
 * param (in) para: UINT32  para of call back function
 * return  void
 */
VOID Hal_NFFS_File_Play_Onetime(const char *path, HAL_SPEAKER_GAIN volume, CB_FUNC func, void *para)
{
    audio_info_t *p = &audio_info;
    uint32_t file_size = 0;

    if (p->closed == 0) {
        Hal_NFFS_File_Play_End();
        p->closed = 1;
    }

    if(NULL == audio_msg) {
        audio_msg = (lv_audio_msg_t *)lv_mem_alloc(sizeof(lv_audio_msg_t));
        if(NULL == audio_msg) {
            printf("%s: audio_msg malloc memory failed!!!\n", __FUNCTION__);
            return;
        }
    }
    memset(audio_msg, 0, sizeof(lv_audio_msg_t));

    if(LV_FS_RES_OK != lv_fs_open(&p->fp, path, LV_FS_MODE_RD)) {
        printf("%s: lv_fs_open %s failed\n", __FUNCTION__, path);
        return;
    }

    if(LV_FS_RES_OK != lv_fs_size (&p->fp, &file_size)) {
        lv_fs_close(&p->fp);
        printf("%s: lv_fs_size %d get failed\n", __FUNCTION__, file_size);
        return;
    } else {
        if(MAX_BYTES_TO_READ < file_size) {
            lv_fs_close(&p->fp);
            printf("%s: file_size(%d) exceed 1M\n", __FUNCTION__, file_size);
            return;
        }
    }
    printf("%s:file_size = %d\n", __FUNCTION__, file_size);
    audio_msg->buffer = lv_mem_alloc(file_size);
    if(NULL == audio_msg->buffer) {
        printf("%s: buffer malloc memory failed!!!\n", __FUNCTION__);
        return;
    }
    memset(audio_msg->buffer, 0, file_size);

    lv_fs_read(&p->fp, audio_msg->buffer, file_size, &audio_msg->len);
    lv_fs_close(&p->fp);
    memset(&audio_info.fp, 0, sizeof(lv_fs_file_t));

    Hal_Tone_Play_Onetime((uint32_t *)audio_msg->buffer, audio_msg->len, volume, func, para);
}

/**
 * play file not in flash fs repeatedly
 * param (in) path: const char*
 * param (in) volume: HAL_SPEAKER_GAIN
 * return  void
 */
VOID Hal_NFFS_File_Play_Repeat(const char *path, HAL_SPEAKER_GAIN volume)
{
    audio_info_t *p = &audio_info;
    uint32_t file_size = 0;

    if (p->closed == 0) {
        Hal_NFFS_File_Play_End();
        p->closed = 1;
    }

    if(NULL == audio_msg) {
        audio_msg = (lv_audio_msg_t *)lv_mem_alloc(sizeof(lv_audio_msg_t));
        if(NULL == audio_msg) {
            printf("%s: audio_msg malloc memory failed!!!\n", __FUNCTION__);
            return;
        }
    }
    memset(audio_msg, 0, sizeof(lv_audio_msg_t));

    if(LV_FS_RES_OK != lv_fs_open(&p->fp, path, LV_FS_MODE_RD)) {
        printf("%s: lv_fs_open %s failed\n", __FUNCTION__, path);
        return;
    }

    if(LV_FS_RES_OK != lv_fs_size (&p->fp, &file_size)) {
        lv_fs_close(&p->fp);
        printf("%s: lv_fs_size %d get failed\n", __FUNCTION__, file_size);
        return;
    } else {
        if(MAX_BYTES_TO_READ < file_size) {
            lv_fs_close(&p->fp);
            printf("%s: file_size(%d) exceed 1M\n", __FUNCTION__, file_size);
            return;
        }
    }
    printf("%s:file_size = %d\n", __FUNCTION__, file_size);
    audio_msg->buffer = lv_mem_alloc(file_size);
    if(NULL == audio_msg->buffer) {
        printf("%s: buffer malloc memory failed!!!\n", __FUNCTION__);
        return;
    }
    memset(audio_msg->buffer, 0, file_size);

    lv_fs_read(&p->fp, audio_msg->buffer, file_size, &audio_msg->len);
    lv_fs_close(&p->fp);
    memset(&audio_info.fp, 0, sizeof(lv_fs_file_t));

    Hal_Tone_Play_Repeat((uint32_t *)audio_msg->buffer, audio_msg->len, volume);
}

/**
 * stop playing file not in flash fs
 * param (in) void:
 * return  void
 */
VOID Hal_NFFS_File_Play_End(VOID)
{
    Hal_Tone_Play_End();
}

/**
 * set volume
 * param (out) UINT16 volume
 * return  void
 */
VOID Hal_Audio_SetVolume(UINT16 volume)
{
#if USE_LV_BLUETOOTH != 0
    if(bt_get_headset_connect()) {
        MCI_AudioSetVolume_Bt(to_mci_volume(volume));
    } else
#endif
    {
        MCI_AudioSetVolume(volume);
    }
}

/**
 * audio pause
 * param (out) VOID
 * return  void
 */
VOID Hal_Audio_Pause(VOID)
{
    MCI_AudioPause();
}

/**
 * audio resume
 * param (out) VOID
 * return  void
 */
VOID Hal_Audio_Resume(VOID)
{
    MCI_AudioResume(0);
}

/**
 * get play information
 * param (out) hal_play_info_t* PlayInformation
 * return  void
 */
VOID Hal_Audio_Get_PlayInformation(hal_play_info_t* PlayInformation)
{
    MCI_AudioGetPlayInformation((MCI_PlayInf*)PlayInformation);
}

VOID Hal_File_Play_Onetime(const char *path, HAL_SPEAKER_GAIN volume, CB_FUNC func, void *para, int32_t PlayProgress)
{
    audio_info_t *p = &audio_info;
    int32_t ret = 0;

    if (p->closed == 0) {
        //printf("%s: Already played, invoke Hal_file_play_end\n", __FUNCTION__);
        if(NULL != p->fp.file_d) {
            lv_fs_close(&p->fp);
            memset(&audio_info.fp, 0, sizeof(lv_fs_file_t));
        }
        Hal_File_Play_End();
        p->closed = 1;
    }

    if(LV_FS_RES_OK != lv_fs_open(&p->fp, path, LV_FS_MODE_RD)) {
        printf("%s: lv_fs_open %s failed\n", __FUNCTION__, path);
        if(func)
            func(NULL);
        return;
    }

    Hal_Sema_Lock(p->sema);
    p->finished = 0;
    Hal_Sema_Unlock(p->sema);

    p->cb = func;
    p->cb_arg = para;

    lv_fs_file_t *pFp = (lv_fs_file_t *)&(audio_info.fp);
    int fhandler =  *(int *)pFp->file_d;

#if USE_LV_BLUETOOTH != 0
    extern UINT32 MCI_AudioPlayWithBtConnected(INT32 OutputPath, HANDLE fileHandle, mci_type_enum fielType,
                                               MCI_AUDIO_PLAY_CALLBACK_T callback, INT32 PlayProgress);

    if(bt_get_headset_connect()) {
        MCI_AudioOpenPath(MCI_PATH_BT);
        MCI_init_btVolLevel_table(MCI_SPK_MAX, 11);
        MCI_AudioSetVolume_Bt(to_mci_volume(volume));
        ret = MCI_AudioPlayWithBtConnected(MCI_PATH_BT, fhandler,  (mci_type_enum)0, audio_play_cb, PlayProgress);
    } else
#endif
    {
        MCI_AudioOpenPath(MCI_PATH_LSP);
        MCI_AudioSetVolume(to_mci_volume(volume));
        ret = MCI_AudioPlay(MCI_PATH_LSP, fhandler, (mci_type_enum)0, audio_play_cb, PlayProgress);
    }
    if (ret != MCI_ERR_NO) {
        printf("%s: MCI_AudioPlay Failed ret:%d!\n", __FUNCTION__, ret);
    }
    p->closed = 0;
}

VOID Hal_File_Audio_Seekto(int32_t seekMs)
{
    int32_t ret = 0;
    ret = MCI_AudioSeekTo(seekMs);
    if (ret != MCI_ERR_NO) {
        printf("%s: MCI_AudioSeekTo Failed ret:%d!\n", __FUNCTION__, ret);
    }
}

VOID Hal_File_Play_End(VOID)
{
    audio_info_t *p = &audio_info;
    int32_t ret = 0;

    if (p->closed == 0) {
        ret = MCI_AudioStop();
        if (ret != MCI_ERR_NO) {
            printf("%s: MCI_AudioStop failed,  ret:%d!\n", __FUNCTION__, ret);
        }
    }

    p->closed = 1;
    lv_fs_close(&p->fp);
    memset(&audio_info.fp, 0, sizeof(lv_fs_file_t));
    Hal_Sema_Lock(p->sema);
    p->finished = 1;
    Hal_Sema_Unlock(p->sema);
}

static void audio_record_callback(MCI_EVNET_T event, MCI_INFO_T info_type, int32_t value)
{
    Hal_Sema_Lock(record_info.sema);
    record_info.has_event = 1;
    record_info.event = event;
    record_info.info_type = info_type;
    record_info.value = value;
    Hal_Sema_Unlock(record_info.sema);
}

void Hal_File_Record_With_Format_Req(const char *path, AUDIO_RECORD_CB_FUNC func, uint8_t format)
{
    lv_fs_file_t *pFp = &(audio_info.fp);

    if (pFp->file_d) {
        //printf("%s: Already recorded, return\n", __FUNCTION__);
        return;
    }

    if(LV_FS_RES_OK != lv_fs_open(pFp, path, LV_FS_MODE_WR)) {
        printf("%s: lv_fs_open %s failed\n", __FUNCTION__, path);
        if(func) func(MCI_EVENT_ERROR, MCI_INFO_ERROR, 0);
        return;
    }

    Hal_Sema_Lock(record_info.sema);
    record_info.has_event = 0;
    record_info.cb = func;
    Hal_Sema_Unlock(record_info.sema);
    MCI_AudioRecordStart(*(int *)pFp->file_d, (mci_type_enum)format, 0, audio_record_callback, NULL);
    return;
}

void Hal_File_Record_Start_Req(const char *path, AUDIO_RECORD_CB_FUNC func)
{
    Hal_File_Record_With_Format_Req(path, func, (mci_type_enum)HAL_AUDIO_FORMAT_AMR);
}

UINT32 Hal_File_Record_Stop_Req(UINT32 *pFile_size)
{
    lv_fs_file_t *pFp = &audio_info.fp;
    UINT32 ret = MCI_AudioRecordStop();

    if(pFp->file_d) {
      if(pFile_size) lv_fs_size(pFp, pFile_size);
      //printf("%s: file size=%d\n", __FUNCTION__, *pFile_size);
      lv_fs_close(pFp);
    }
    memset(&audio_info.fp, 0, sizeof(lv_fs_file_t));

    if(*pFile_size == 0) {
       return MCI_ERR_ERROR;
    }

    return ret;
}

/*
*  Get Audio Duration Time from file (C:\\ or D:\\)
*/
uint8_t Hal_File_Get_AudioDurationTime(const char *path, uint32_t *pDurationTime)
{
    MCI_ProgressInf audioInfo = {0};
    lv_fs_file_t audiofile;
    int fhandler =  0;
    uint8_t res = LV_FS_RES_UNKNOWN;

    res = lv_fs_open(&audiofile, path, LV_FS_MODE_RD);
    if(res == LV_FS_RES_OK) {
        fhandler =  *(int *)audiofile.file_d;
        MCI_AudioGetDurationTime(fhandler, 0, 0, 0, &audioInfo);
        *pDurationTime = audioInfo.DurationTime;
        lv_fs_close(&audiofile);
    } else {
        *pDurationTime = 0;
    }

    return res;
}

/**
 * start record to buffer
 * param (in) UINT8 *: the buffer pointer.
 * param (in) UINT32: buffer size, bytes
 * param (in) UINT32: maxDuration, ms
 * param (in) AUDIO_RECORD_CB_FUNC: callback function, used to get current size and duration.
 * return  void
 */
VOID Hal_Record_Buffer_With_Format_Req( UINT8 *buffer, UINT32 bufsize, UINT32 maxDurationMs, AUDIO_RECORD_CB_FUNC func, uint8_t format)
{
    UINT32 ret;

    if(buffer == NULL) {
        printf("%s: input param invalid.\n", __FUNCTION__);
        return;
    }

    /*mci_type_enum is AMR*/
    Hal_Sema_Lock(record_info.sema);
    record_info.has_event = 0;
    record_info.cb = func;
    Hal_Sema_Unlock(record_info.sema);
    ret = MCI_AudioBufferRecordStart(buffer, bufsize, maxDurationMs, (mci_type_enum)format, 0, audio_record_callback, NULL);
    if (ret != MCI_ERR_NO) {
        printf("%s: Record failed,  ret:%d!\n", __FUNCTION__, ret);
        return;
    }
}

VOID Hal_Record_Buffer_Start_Req( UINT8 *buffer, UINT32 bufsize, UINT32 maxDurationMs, AUDIO_RECORD_CB_FUNC func)
{
    Hal_Record_Buffer_With_Format_Req(buffer, bufsize, maxDurationMs, func, (mci_type_enum)HAL_AUDIO_FORMAT_AMR);
}


/**
 * stop record to buffer
  * param (out) UINT32 *: recorded data size.
  * param (out) UINT8 *: recorded duration, unit is ms.
  * return UINT32
  * Note: After stop, please free the record buffer.
  */
UINT32 Hal_Record_Buffer_Stop_Req(UINT32 *recSize, UINT32 *durationMs)
{
    if(recSize == NULL || durationMs == NULL) {
        printf("%s: input param invalid.\n", __FUNCTION__);
        return MCI_ERR_INVALID_PARAMETER;
    }

    return MCI_AudioBufferRecordStop(recSize, durationMs);
}

VOID Hal_NFFS_File_Record_Start_Req(const char *path, AUDIO_RECORD_CB_FUNC func)
{
    lv_fs_file_t *pFp = &(audio_info.fp);

    if (pFp->file_d) {
        //printf("%s: Already recorded, return\n", __FUNCTION__);
        return;
    }

    lv_fs_res_t ret = lv_fs_open(&nffs_file, path, LV_FS_MODE_WR);
    if(ret != LV_FS_RES_OK) {
        printf("%s: lv_fs_open %s failed, cause %d\n", __FUNCTION__, path, ret);
        if(func) func(MCI_EVENT_ERROR, MCI_INFO_ERROR, 0);
        return;
    }

    ret = lv_fs_open(pFp, NFFS_FILE_RECORD_TEMP_PATH, LV_FS_MODE_APPEND);
    if(ret != LV_FS_RES_OK) {
        printf("%s: lv_fs_open temp path failed, cause %d\n", __FUNCTION__, ret);
        lv_fs_close(&nffs_file);
        lv_fs_remove(path);
        memset(&nffs_file, 0, sizeof(lv_fs_file_t));
        if(func) func(MCI_EVENT_ERROR, MCI_INFO_ERROR, 0);
        return;
    }

    Hal_Sema_Lock(record_info.sema);
    record_info.has_event = 0;
    record_info.cb = func;
    Hal_Sema_Unlock(record_info.sema);
    MCI_AudioRecordStart(*(int *)pFp->file_d, (mci_type_enum)3, 0, audio_record_callback, NULL); /* AMR */
    return;
}

UINT32 Hal_NFFS_File_Record_Stop_Req(UINT32 *pFile_size)
{
    lv_fs_file_t *pFp = &audio_info.fp;
    UINT32 ret = MCI_AudioRecordStop();

    UINT32 data_size = 0;
    if(pFp->file_d && nffs_file.file_d) {
        lv_fs_size(pFp, &data_size);
        if(data_size > 0) {
            void * data = lv_mem_alloc(data_size);
            if(data == NULL) {
                data_size = 0;
                printf("%s: data malloc failed\n", __FUNCTION__);
                ret = MCI_ERR_OUT_OF_MEMORY;
            } else {
                lv_fs_seek(pFp, SEEK_SET);
                lv_fs_read(pFp, data, data_size, NULL);
                lv_fs_seek(&nffs_file, SEEK_SET);
                lv_fs_write(&nffs_file, data, data_size, NULL);
                lv_mem_free(data);
            }
        } else {
            printf("%s: file size is zero\n", __FUNCTION__);
            ret = MCI_ERR_ERROR;
        }
    } else {
        printf("%s: file not opened\n", __FUNCTION__);
        ret = MCI_ERR_ACTION_NOT_ALLOWED;
    }
    if(nffs_file.file_d) {
        lv_fs_close(&nffs_file);
        memset(&nffs_file, 0, sizeof(lv_fs_file_t));
    }
    if(pFp->file_d) {
        lv_fs_close(pFp);
        lv_fs_remove(NFFS_FILE_RECORD_TEMP_PATH);
        memset(pFp, 0, sizeof(lv_fs_file_t));
    }
    if(pFile_size) *pFile_size = data_size;

    return ret;
}

VOID Hal_Switch_Audio_Speaker(HAL_SPEAKER_GAIN volume)
{
    printf("%s\n", __FUNCTION__);
    MCI_AudioOpenPath(MCI_PATH_LSP);
    MCI_AudioSetVolume(to_mci_volume(volume));
}

/**
 * call speaker control
 * param (in) callspeaker:  HAL_CALL_SPEAKER
 * param (in) volume:  HAL_SPEAKER_GAIN
 * return  void
 */
VOID Hal_Call_Speaker(HAL_CALL_SPEAKER callspeaker, HAL_SPEAKER_GAIN volume)
{
    if (HAL_CALL_SPK_ON == callspeaker)
    {
        printf("Switch to SPEAKER.\n");
        AUDIOHAL_AIF_DEVICE_CFG_T config;
        memset((void *)&config, 0, sizeof(AUDIOHAL_AIF_DEVICE_CFG_T));
        AudioHAL_AifOpen(AUDIOHAL_ITF_LOUDSPEAKER, &config);
    } else {
        printf("Switch to HEADPHONE\n");
        AUDIOHAL_AIF_DEVICE_CFG_T config;
        memset((void *)&config, 0, sizeof(AUDIOHAL_AIF_DEVICE_CFG_T));
        AudioHAL_AifOpen(AUDIOHAL_ITF_RECEIVER, &config);
    }

    printf("MCI_AudioSetVolume: %d\n", volume);
    MCI_AudioSetVolume(to_mci_volume(volume));
}

/**
 * call speaker control
 * param (in) status: HAL_VOICE_CALL_STATUS
 * return  void
 */

VOID Hal_Set_Voice_Call_Status(HAL_VOICE_CALL_STATUS status)
{
#if USE_SDK_API
    AudioHAL_AifSetPhoneStatus(status);
#endif
}

/* HAL audio&tone interface end */

/* HAL NVRAM interface begin */
/**
 * write to NVRAM
 * param (in) addr:  UINT32*
 * param (in) data:  UINT32*
 * param (in) len:   UINT32
 * return  UINT32: bytes have been writen
 */
UINT32 Hal_Nvram_Write(UINT8 *addr, UINT8 *data, UINT32 len)
{
    // to do
    return(len);
}

/**
 * read data from NVRAM
 * param (in) addr:  UINT32*
 * param (out) data:  UINT32*
 * param (in) len:   UINT32
 * return  UINT32: bytes have been read
 */
UINT32 Hal_Nvram_Read(UINT8 *addr, UINT8 *data, UINT32 len)
{
    // to do
    return(len);
}
/* HAL NVRAM interface end */

/* HAL OSA interface begin */
// message

/**
 * send message to task 'id'
 * param (in) id: TASK_ID
 * param (in) message: VOID*
 * return  void
 */
VOID Hal_Send_Message(TASK_ID id, VOID *message)
{
    unsigned long _id = id;
    TASK_HANDLE *task;

    task = (TASK_HANDLE *)_id;
    uos_send_msg (message, task->nMailBoxId, UOS_SEND_MSG);
}

/**
 * receive message from task 'id'
 * param (in) id: TASK_ID
 * param (in) IsBlock: BOOL true means block
 * return  VOID*
 */
VOID *Hal_Receive_Message(TASK_ID id, BOOL IsBlock)
{
    unsigned long _id = id;
    TASK_HANDLE *task;
    uint32_t flag;

    task = (TASK_HANDLE *)_id;
    flag = IsBlock?UOS_WAIT_FOREVER:UOS_NO_WAIT;

    return uos_wait_msg (NULL, task->nMailBoxId, flag);
}

/**
 * get messages count of task 'id'
 * param (in) id: TASK_ID
 * return  UINT32
 */
UINT32 Hal_Get_Message_Count(TASK_ID id)
{
    unsigned ret = 0;

#if USE_SDK_API
    TASK_HANDLE *task;
    task = (TASK_HANDLE *)id;
    ret = uos_get_msgcnt (task->nMailBoxId);
#endif
    return ret;
}

// timer
static void Hal_Timer_Cb(lv_task_t *task)
{
    hal_timer_param_t * timerparam = task->user_data;
    BOOL repeat = timerparam->repeat;
    if(!repeat) timerparam->valid = false;
    timerparam->cb(timerparam->param);
    if(!repeat) lv_mem_free(timerparam);
}

/**
 * create and start timer
 * param (in) timeout: UINT32 (ms)
 * param (in) callback: CB_FUNC*
 * param (in) para: callback param
 * param (in) repeat: BOOL
 * return  TIMER_ID
 */
TIMER_ID Hal_Timer_Start(UINT32 timeout, CB_FUNC callback, void* param, BOOL repeat)
{
    LV_ASSERT_NULL(callback);
    hal_timer_param_t * timerparam = (hal_timer_param_t *)lv_mem_alloc(sizeof(hal_timer_param_t));

    timerparam->cb = callback;
    timerparam->param = param;
    timerparam->repeat = repeat;
    timerparam->valid = true;

    lv_task_t *task = lv_task_create(Hal_Timer_Cb, timeout, LV_TASK_PRIO_OFF, timerparam);
    if(repeat == 0){
        lv_task_once(task);
    }
    lv_task_set_prio(task, LV_TASK_PRIO_HIGHEST);

    return task;
}

/**
 * stop and delete timer
 * param (in) id: TIMER_ID
 * return  void
 */
VOID Hal_Timer_Stop(TIMER_ID id)
{
    if(NULL == id) return;
    hal_timer_param_t * param = ((lv_task_t *)id)->user_data;
    if(param->repeat || param->valid == true) lv_mem_free(param);
    lv_task_del((lv_task_t *)id);
}

/**
 * create semaphore
 * param (in) id: SEMA_ID
 * param (in) InitCnt: UINT32 initial value of semaphone
 * return  SEMA_ID *
 */
SEMA_ID *Hal_Sema_Create(UINT32 InitVal)
{
    unsigned long id;

    id = uos_new_mutex();
    if (InitVal != 0) {
        uos_take_mutex(id);
    }

    return (SEMA_ID *)id;
}

/**
 * destroy semaphore
 * param (in) id: SEMA_ID*
 * return  void
 */
VOID Hal_Sema_Destroy(SEMA_ID *id)
{
    uos_free_mutex((uint32_t)id);
}

/**
 * lock semaphore
 * param (in) id: SEMA_ID*
 * return  void
 */
VOID Hal_Sema_Lock(SEMA_ID *id)
{
    uos_take_mutex((uint32_t)id);
}

/**
 * unlock semaphore
 * param (in) id: SEMA_ID*
 * return  void
 */
VOID Hal_Sema_Unlock(SEMA_ID *id)
{
    uos_release_mutex((uint32_t)id);
}

/* HAL OSA interface end */
#define SOFT_REBOOT_FLAG    0xF
extern uint8_t boot_flag_get(void);
extern void boot_flag_set(uint8_t value);

/**
 * power off
 * param (in) type: HAL_POWER_OFF_TYPE
 * return  void
 */
VOID Hal_Power_Off(HAL_POWER_OFF_TYPE type)
{
    if(type == HAL_TYPE_REBOOT || type == HAL_TYPE_FACTORY_RESET) {
        boot_flag_set(SOFT_REBOOT_FLAG);
    }
    board_power_off((int)type);
}

watch_ambient_type_t ambient_flag = AMBIENT_OFF;
#if !FAKE_BATTERY
static TASK_HANDLE *ambient_task_id __attribute((unused)) = NULL;
#define AMBIENT_IDLE_TIME 60*1000
static void ambient_thead_task(void *param)
{
    while(1) {
        printf("Ambient uos thread enter sleep\n");
        uos_sleep(MS_TO_TICKS(AMBIENT_IDLE_TIME));
        if(pm_state_flag != -1) {
            printf("Ambient wakeup gui main thread\n");
            uos_set_flag(pm_state_flag, PM_RESUME_AMBIENT, UOS_FLAG_OR);
        }
    }
}
static VOID ambient_task_create(VOID)
{
    if(NULL == ambient_task_id) {
#if UI_IN_XIP
        static lv_xip_thread param1;
        param1.func = ambient_thead_task;
        param1.params = NULL;
        ambient_task_id = uos_create_task(
             lv_xip_thread_entry,
             &param1,
             0,
             0x2000,
             240,
             "ambient");
#else
        ambient_task_id = uos_create_task(ambient_thead_task, NULL, 0, 0x2000, 240, "ambient");
#endif
    }
}
static VOID ambient_task_destroy(VOID)
{
    if(ambient_task_id) {
        uos_delete_task(ambient_task_id);
        ambient_task_id = NULL;
    }
}
#endif
VOID Hal_ambient_flag_set(watch_ambient_type_t flag)
{
    ambient_flag = flag;
    //need add code to write nvm file
    printf("Hal_ambient_flag_set: %d\n", flag);
#if !FAKE_BATTERY
    if(AMBIENT_ON == ambient_flag) {
      ambient_task_create();
    } else if(AMBIENT_OFF == ambient_flag) {
      ambient_task_destroy();
    }
#endif
}

watch_ambient_type_t Hal_ambient_flag_get(void)
{
    return ambient_flag;
}

void my_print(lv_log_level_t level, const char *file, uint32_t line, const char *dsc)
{
    lv_mem_monitor_t mon;

    if (level >= LV_LOG_LEVEL_WARN) // change output level here
    {
        printf("************* %s, %d, %s\n", file, line, dsc);

        lv_mem_monitor(&mon);
        printf("used: %6d (%3d %%), frag: %3d %%, biggest free: %6d\n",
            (int)mon.total_size - mon.free_size,mon.used_pct,mon.frag_pct,
            (int)mon.free_biggest_size);
    }
}

#if FAKE_BATTERY
extern int bat_remain;
static void bat_polling(lv_task_t *par)
{
    (void)cbLowPower;
    if (pmic_get_chg_status()) {
        bat_remain += 1;
        if (bat_remain > 100) {
            bat_remain = 100;
        }
    } else {
        bat_remain -= 1;
        if (bat_remain < 0) {
            bat_remain = 0;
        }
    }
}
#else
static int hal_usb_plug_cb(unsigned int action)
{
    //action: 1: DEVUSB_DETECT_ACTION_PLUGIN / 2: DEVUSB_DETECT_ACTION_PLUGOUT
    //printf("irq: %d\n", action);
    charger_irq_cb();

    return 0;
}

extern bool watch_modem_call_get_call_active_status(void);
static unsigned int hal_get_call_status_cb(unsigned int SimId)
{
    return watch_modem_call_get_call_active_status();
}

static int hal_battery_cb(unsigned char bcs,unsigned char bcl,unsigned short batLevel)
{
    // bcl: battery percent
    battery_percent = bcl;

    if(IschargerDebugEn()) {
        uint32_t battery_val_mV = pm812_get_batt_vol();

        if(PMIC_IS_PM813()||PMIC_IS_PM813S())
        {
            uint32_t charge_cur_mA = PMIC_IS_PM813() ? pm813_get_Icharge_meas_cur_mA() : pm813s_get_Icharge_meas_cur_mA();
            uint8_t var;
        /*
        offset 0x25: bit5~4, val=01: means 4.35v
        */
            NingboRead(NINGBO_BASE_Reg, 0x25, &var);
            printf("[hal_battery_cb] PMIC[0x25] = 0x%x, ", var);
            NingboRead(NINGBO_BASE_Reg, 0x26, &var);
            printf("[0x26] = 0x%x, ", var);
            NingboRead(NINGBO_BASE_Reg, 0x5B, &var);
            printf("[0x5B] = 0x%x, ", var);
            NingboRead(NINGBO_BASE_Reg, 0x59, &var);
            printf("[0x59] = 0x%x, ", var);
            NingboRead(NINGBO_BASE_Reg, 0x23, &var);
            printf("[0x23] = 0x%x, ", var);
            printf("[bcs=%d, bcl=%d, batlevel=%d], charge_cur_mA=%d(charging=%d), current vol_withoutCharger=%d\n",
            bcs, bcl, batLevel, charge_cur_mA, pm813_measured_current_means_charging(), battery_val_mV);
        }
        else  //for pm803 /pm802 use not pmic charger
            printf("[bcs=%d, bcl=%d, batlevel=%d], current vol=%d\n",
            bcs, bcl, batLevel, battery_val_mV);
    }

    return 0;
}

static void bat_charger_task(void *param)
{
    while(1) {
        pm_BatteryMonitorProc();
        if((!pmic_get_chg_status()) && (cbLowPower != NULL) && (battery_percent <= LOW_BAT_THD) && (lv_pm_get_state() == PM_SUSPEND)) {
            /*if usb is not on, when low power, resume ui to shutdown */
            cbLowPower((void *)battery_percent);
        }
        uos_sleep(MS_TO_TICKS(charger_get_Time_To_MMI_Evt()));
    }
}

typedef void (*UsbWkpCallback)(void);
static void bat_charger_init(void)
{
    ChargerCallbackRegister(hal_usb_plug_cb, hal_get_call_status_cb, hal_battery_cb);
#if PMIC_UV_IRQ_EN != 0
    Ningbo_INT_CALLBACK_REGISTER(NINGBO_VBUS_OVP_INT, charger_pmic_ovp_irq_cb);
#endif
    pm_BatteryInit();
#if UI_IN_XIP
    static lv_xip_thread param2;
    param2.func = bat_charger_task;
    param2.params = NULL;
    bat_task = uos_create_task(
             lv_xip_thread_entry,
             &param2,
             0,
             0x2000,
             240,
             "bat_monitor");
#else
    bat_task = uos_create_task(bat_charger_task, NULL, 0, 0x2000, 240, "bat_monitor");
#endif
}
#endif

/**only for craneg A1**/
#if defined(USE_VAD) && (USE_VAD > 0)
static void hal_voice_wakeup_callback(unsigned int key)
{
    (void)key;
    if(pm_state_flag != -1) {
        uos_set_flag(pm_state_flag, PM_RESUME_VAD, UOS_FLAG_OR);
    }
}
int Hal_Voice_Wakeup_Vad_On(void)
{
    return AudioHAL_AifSet_VAD(1);
}

int Hal_Voice_Wakeup_Vad_Off(void)
{
    return AudioHAL_AifSet_VAD(0);
}

int Hal_Voice_Wakeup_Vad_Get_Status(void)
{
    return AudioHAL_AifGet_VAD();
}
#endif

#if USE_CRANE_CAMERA
static void hal_camera_suspend(void *arg)
{
    if (camera_on_flag)
        mci_camera_stoppreview();
}

static void hal_camera_resume(void *arg)
{
    if (camera_on_flag)
        mci_camera_openpreview();
}
#endif

VOID Hal_Init(VOID)
{
    LV_TASK_CREATE_MUTEX;
    // register print function for log, output level could be set in my_print
    AUDIOHAL_AIF_DEVICE_CFG_T config;
    memset((void *)&config, 0, sizeof(AUDIOHAL_AIF_DEVICE_CFG_T));
    AudioHAL_AifOpen(AUDIOHAL_ITF_LOUDSPEAKER, &config);

    memset(&audio_info, 0x0, sizeof(audio_info_t));
    audio_info.sema = Hal_Sema_Create(0);
    audio_info.closed = 1;
    audio_info.finished = 1;

    record_info.sema = Hal_Sema_Create(0);
    record_info.has_event = 0;
    record_info.cb = NULL;

    memset(&vibrator_info, 0x0, sizeof(vibrator_info_t));
    vibrator_info.type = VIBRATOR_PLAY_REPEAT;
    vibrator_info.status = VIBRATOR_STOP;

    memset(&sd_info, 0, sizeof(sd_info_t));
    sd_info.sema = Hal_Sema_Create(0);
    sd_info.user = 1;

    hal_task = lv_task_create((TASK_CB_FUNC)hal_task_fn, 100, LV_TASK_PRIO_MID, NULL);

    if (isSysRestartByRdProduction() == 0) {
        // for simple memcheck, memcpy and memset overlay
        indication_task_init();

#if FAKE_BATTERY
        lv_task_create(bat_polling, 1000 * 60, LV_TASK_PRIO_MID, NULL); //change every 1 min
#else
        bat_charger_init();
#endif

#if defined(USE_VAD) && (USE_VAD > 0)
/**only for craneg A1**/
        AudioHAL_AifBindKWS_CB(hal_voice_wakeup_callback);
#endif

#if USE_CRANE_CAMERA
        lv_pm_info_t pm_info;

        cam_hw_version_init();
        cam_offline_init();
        mci_camera_register_init();

        camera_on_flag = 0;
        pm_info.suspend = hal_camera_suspend;
        pm_info.resume = hal_camera_resume;
        pm_info.data = NULL;
        lv_pm_register(&pm_info);
#endif
    }

#if USE_LV_BLUETOOTH != 0
    bt_config_firmware();
#endif

#if USE_LV_WLAN != 0
    hal_wlan_config();
#endif

#if USE_SDK_API
    pm_mutex = uos_new_mutex();
#endif
}

HAL_POWERUP_REASON Hal_Powerup_Get_Reason(VOID)
{
    if (isSysRestartByNormal()) {
        if (PMIC_IS_PM803()) {
            uint8_t flag = boot_flag_get();
            if(SOFT_REBOOT_FLAG == flag) {
                return HAL_POWERUP_REBOOT;
            }
        }
        return HAL_POWERUP_ONKEY;
    }
    if (isSysRestartByNormal()) return HAL_POWERUP_ONKEY;
    if (isSysRestartByCharging()) return HAL_POWERUP_USB;
    if (isSysRestartByAlarm()) return HAL_POWERUP_RTC_ALARM;
    if (isSysRestartByError()) return HAL_POWERUP_SILENT_RESET;
    if (isSysRestartByRdProduction()) return HAL_POWERUP_PRODUCTION;

    return HAL_POWERUP_UNKNOWN;
}

int Hal_Camera_Start_Preview(cam_start_preview_t *params)
{
#if USE_CRANE_CAMERA
    int ret = 0;
    mci_camera_preview_reset();
#if USE_CRANE_CAMERA_MULTI
    mci_camera_set_sensor(params->sensor_id);
#endif
    ret = mci_camera_openpreview();
    if (ret == 0) {
        camera_on_flag = 1;
    } else {
        return -1;
    }
#endif
    return 0;
}

int Hal_Camera_Stop_Preview(UINT8 sensor_id)
{
#if USE_CRANE_CAMERA
    camera_on_flag = 0;
    mci_camera_stoppreview();
#endif
    return 0;
}

int Hal_Camera_Preview(UINT8 *rgbBuf)
{
#if USE_CRANE_CAMERA
    return (int)mci_camera_preview(rgbBuf, LV_HOR_RES, LV_VER_RES);
#else
    return 0;
#endif
}

int Hal_Camera_Preview2(UINT8 *rgbBuf, int width, int height)
{
#if USE_CRANE_CAMERA
    return (int)mci_camera_preview(rgbBuf, width, height);
#else
    return 0;
#endif
}

int Hal_Camera_Preview_Yuv_Size(int *width, int *height)
{
#if USE_CRANE_CAMERA
    return (int)mci_camera_preview_yuv_size(width, height);
#else
    return 0;
#endif
}

int Hal_Camera_Preview_Qrcode_Scan(UINT8 *yuvBuf)
{
#if USE_CRANE_CAMERA
    int pre_w = LV_HOR_RES;
    int pre_h = LV_VER_RES;
#if USE_CRANE_CAMERA_FULL_SIZE
    Hal_Camera_Preview_Yuv_Size(&pre_w,&pre_h);
#endif
    return (int)mci_camera_preview_qrcode_scan(yuvBuf, pre_w, pre_h);
#else
    return 0;
#endif
}

int Hal_Camera_Capture(cam_capture_t *params, int file_handle)
{
    int error = 0;

#if USE_CRANE_CAMERA
    error = mci_camera_capture(params, file_handle);
#endif

    return error;
}

uint8_t Hal_Camera_Get_On_Flag(void)
{
#if USE_CRANE_CAMERA
    return camera_on_flag;
#else
    return 0;
#endif
}

int Hal_Camera_Start_FullSizeStream(UINT8 sensor_id, CAM_RECORD_PARMETERS_STRUCT *pRecordParam, UINT32 format)
{
#if USE_CRANE_CAMERA
    int ret = 0;
    mci_camera_preview_reset();
#if USE_CRANE_CAMERA_MULTI
    mci_camera_set_sensor(sensor_id);
#endif
    ret = mci_camera_start_full_size_stream(pRecordParam, format);
    if (ret == 0) {
        camera_on_flag = 1;
    } else {
        return -1;
    }
#endif
    return 0;
}

int Hal_Camera_Stop_FullSizeStream(void)
{
#if USE_CRANE_CAMERA
    camera_on_flag = 0;
    mci_camera_stop_full_size_stream();
#endif
    return 0;
}

void pm_lock(void)
{
#if USE_SDK_API
    uos_take_mutex(pm_mutex);
#endif
}

void pm_unlock(void)
{
#if USE_SDK_API
    uos_release_mutex(pm_mutex);
#endif
}

uint32_t Hal_Pm_Suspend(VOID)
{
    uint32_t ret = 0;

#ifndef BUILD_IN_PC_SIMULATOR
    audio_info_t *p = &audio_info;
    int finished;

    finished = p->finished;
    if ((finished == 0) && (p->closed == 0)) {
        if ((p->fp.file_d) && *(int *)p->fp.file_d > 0) {
            Hal_File_Play_End();
        } else {
            Hal_Tone_Play_End();
        }
        if (p->cb) {
            p->cb(p->cb_arg);
        }
    }
#endif

    pm_lock();

#if USE_SDK_API
    extern TASK_HANDLE *gui_task_id;

    printf("check Q\n");
    if(0 != Hal_Get_Message_Count((TASK_ID)gui_task_id))
    {
        pm_unlock();
        return PM_RESUME_UI;
    }
#endif

    ret = lv_pm_set_state(PM_SUSPEND); /* unlock before stop task */

    return ret;
}

void Hal_Pm_Resume(VOID)
{
    pm_lock();
    lv_pm_set_state(PM_ON);
    pm_unlock();
}

void Hal_Pm_WakeUp(VOID)
{
    extern void sys_pm_wakeup(void);

    pm_lock();
    sys_pm_wakeup();
    pm_unlock();
}

int Hal_Pm_Get_State(VOID)
{
    int ret;

    pm_lock();
    ret = lv_pm_get_state();
    pm_unlock();

    return ret;
}

uint32_t Hal_Timer_Get_Counter(void)
{
    uint32_t t = 0;
 #if USE_SDK_API
    extern UINT32 timerCountRead (TCR_NUMBER tcr_number);
    t = timerCountRead(TS_TIMER_ID);
    return t;
 #else
    t = lv_tick_get()*1000;
    return MICRO_SEC_TO_CLOCK_TICKS_CLK_32KHZ(t);
  #endif
}

void Hal_Nvm_Factory_reset(void)
{
    boot_flag_set(SOFT_REBOOT_FLAG);
    board_power_off(HAL_TYPE_FACTORY_RESET);
}

/*
 * get platform version
 * param (in) void
 * return  uint8_t
*/
uint8_t * Hal_Get_Platform_Version(void)
{
#if defined CRANE_PLATFORM_VERSION
    return((uint8_t *)CRANE_PLATFORM_VERSION);
#else
    static uint8_t platform_version[] = "crane-undefined";

    return(platform_version);
#endif
}

uint32_t Hal_Yuv420_Render(int32_t yuv_width,
                   int32_t yuv_height,
                   int32_t render_width,
                   int32_t render_height,
                   uint8_t *yuv_buf,
                   uint8_t *render_buf,
                   uint8_t semi)
{
    if((render_width == yuv_width) && (render_height == yuv_height)) {
        yuv420_2_rgb565(yuv_width, yuv_height,
               (const unsigned char *)yuv_buf, (unsigned short *)render_buf,
               semi);
        return 1;
    } else {
        int ret;
        uint8_t *pDst = (uint8_t *)lv_mem_aligned_alloc(render_width*render_height*3/2+32, 32);
        if(pDst == NULL) {
            printf("Failed:  mci_camera_preview no enough memory!!!\n");
            return 0;
        }

        ret = yuv420_scale(yuv_width, yuv_height, render_width, render_height,
                   yuv_buf, pDst, semi);
        if(ret == 1) {
            /* convert yuv to rgb */
            yuv420_2_rgb565(render_width, render_height,
                (unsigned char *)pDst, (unsigned short *)render_buf,
                semi);
        }
        lv_mem_aligned_free(pDst);

        return ret;
    }
}

/**
 * audio path control of BT
 * param (in) volume:  HAL_SPEAKER_GAIN
 * return  bool: true means BT path opened
 */
BOOL Hal_Set_BT_Audio_Path(HAL_SPEAKER_GAIN volume)
{
    BOOL ret = false;

#if USE_LV_BLUETOOTH
    if (bt_get_hfp_connect()) {
        if (!appbt_get_sco_status()) {
            appbt_switch_voice_to_headset();
        }

        printf("audio Switch to bluetooth path.\n");
        AUDIOHAL_AIF_DEVICE_CFG_T config;
        memset((void *)&config, 0, sizeof(AUDIOHAL_AIF_DEVICE_CFG_T));
        AudioHAL_AifOpen(AUDIOHAL_ITF_BLUETOOTH, &config);

        printf("MCI_AudioSetVolume: %d\n", volume);
        MCI_AudioSetVolume(to_mci_volume(volume));

        ret = true;
    }
#endif

    return ret;
}
/**
 * disconnect BT audio path of call
 * return  void
 */
BOOL Hal_Disconnect_BT_Audio_Path(VOID)
{
#if USE_LV_BLUETOOTH
    if (appbt_get_sco_status()) {
        int ret = appbt_switch_voice_to_phone();
        if(ret == 0) return true;
    }
    return false;
#else
    return false;
#endif
}

/**
 * Request to add the audio management for app
 * param (in) priority: the priority of register app
 *            Callback:inform the new play priority to app
 * return  current play priority
 */
AUDIO_CTRL_PRIORITY Hal_Audio_Manage_Start_Req(AUDIO_CTRL_PRIORITY priority,AudioCtrlback Callback)
{
    uint8_t i;
    AUDIO_CTRL_PRIORITY current_priority = AUDIO_CTRL_PRIORITY_INVALID;

    for (i = 0;i < AUDIO_CTRL_PRIORITY_MAX;i++)
    {
        if(priority == i)
        {
            audio_management.callbackfunc[i] = Callback;
        }
    }

    for (i = 0; i < AUDIO_CTRL_PRIORITY_MAX; i++)
    {
        if (NULL != audio_management.callbackfunc[i])
        {
            current_priority = (AUDIO_CTRL_PRIORITY)i;
            printf("%s,current_priority: %d\n", __FUNCTION__,current_priority);
            break;
        }
    }

    if(0xFF == old_curr_priority)
    {
        old_curr_priority = current_priority;
    }

    for (i = 0; i < AUDIO_CTRL_PRIORITY_MAX; i++)
    {
        if ((NULL != audio_management.callbackfunc[i])
            && (i != current_priority)
            && (old_curr_priority != current_priority))
        {
            audio_management.callbackfunc[i](current_priority);
        }
    }
    old_curr_priority = current_priority;
    printf("%s,priority: %d,old priority: %d\n", __FUNCTION__,priority,old_curr_priority);
    return current_priority;
}

/**
 * Inform the management that audio has been finished
 * param (in) priority: the priority of register app
 * return  VOID
 */
VOID Hal_Audio_Manage_End_Ind(AUDIO_CTRL_PRIORITY priority)
{
    uint8_t i;
    AUDIO_CTRL_PRIORITY current_priority = AUDIO_CTRL_PRIORITY_INVALID;

    for (i = 0;i < AUDIO_CTRL_PRIORITY_MAX;i++)
    {
        if(priority == i)
        {
            audio_management.callbackfunc[i] = NULL;
        }
    }

    for (i = 0; i < AUDIO_CTRL_PRIORITY_MAX; i++)
    {
        if (NULL != audio_management.callbackfunc[i])
        {
            current_priority = (AUDIO_CTRL_PRIORITY)i;
            printf("%s,current_priority: %d\n", __FUNCTION__,current_priority);
            break;
        }
    }

    for (i = 0; i < AUDIO_CTRL_PRIORITY_MAX; i++)
    {
        if (NULL != audio_management.callbackfunc[i])
        {
            audio_management.callbackfunc[i](current_priority);
        }
    }
    old_curr_priority = current_priority;
    printf("%s,priority: %d,old priority: %d\n", __FUNCTION__,priority,old_curr_priority);
}

/**
 * Init audio management ctrl module
 * param (in) VOID
 * return  VOID
 */
VOID Hal_Audio_Manage_Init(VOID)
{
    printf("%s\n", __FUNCTION__);
    memset(&audio_management,0,sizeof(hal_audio_management_t));
}

/* HAL usb connect interface begin */
/**
 * get usb connect status
 * param (in) void:
 * return  HAL_USB_STATUS
 */
HAL_USB_STATUS Hal_Get_Usb_Status(VOID)
{
    int usb_connect_status;

    usb_connect_status = pmic_get_chg_status();
    // printf("[%s] usb_connect_status = %d[1: connect, 0: disconnect]\n", __func__, usb_connect_status);

    return((1 == usb_connect_status) ? HAL_USB_CONNECTED: HAL_USB_DISCONNECTED);
}

BOOL Hal_Get_Sd_Ready(VOID)
{
    BOOL result = false;
    if(sdcard_is_ready()) {
        result = true;
    }
    return result;
}

BOOL Hal_Get_Sd_Fat_Ok(VOID)
{
    BOOL result = false;
    if(sdcard_fat_is_ok()){
        result = true;
    }
    return result;
}

BOOL Hal_Connect_To_Pc(VOID)
{
    BOOL result = false;
    if(USB_CONNECTED_TO_PC == charger_is_usb()) {
        result = true;
    }
    return result;
}

/* HAL usb connect interface end */

#ifdef LV_MEM_USER_DEBUG
typedef struct
{
    VOID   *Addr;
    UINT32 Size;
    INT8   *Func;
    VOID   *call_from[3];
} Hal_Mem_Info_t;

static uint32_t Hal_Mem_Max_Alloc = 0;
static uint32_t Hal_Mem_Alloc = 0;
static Hal_Mem_Info_t MemInfo[2000] = {};
void Hal_Mem_Alloc_Record(uint32_t size, void * addr, const char * func)
{
    uint16_t i;
    uint16_t size_mod;

    for(i = 0; i < 2000; i++) {
        if(MemInfo[i].Addr == NULL) {
            #include "execinfo.h"
            void * bt[5];
            backtrace(bt, 5);
            MemInfo[i].Addr = addr;
            size_mod = (size%32)?(size/32 + 2):(size/32 + 1);
            MemInfo[i].Size = size_mod * 32;
            MemInfo[i].Func = (INT8 *)func;
            MemInfo[i].call_from[0] = bt[2];
            MemInfo[i].call_from[1] = bt[3];
            MemInfo[i].call_from[2] = bt[4];
            break;
        }
    }

    Hal_Mem_Alloc += MemInfo[i].Size;
    if(Hal_Mem_Max_Alloc < Hal_Mem_Alloc)
        Hal_Mem_Max_Alloc = Hal_Mem_Alloc;
}

void Hal_Mem_Free_Record(const void * addr)
{
    uint16_t i;

    for(i = 0; i < 2000; i++) {
        if(MemInfo[i].Addr == addr) {
            MemInfo[i].Addr = NULL;
            Hal_Mem_Alloc -= MemInfo[i].Size;
            break;
        }
    }
}

void Hal_Mem_Record_Dump(bool detail)
{
    printf("\n===============\nAlloced %d, Max Allocked %d\n", Hal_Mem_Alloc, Hal_Mem_Max_Alloc);
    if(true == detail){
        uint16_t i;
        for(i = 0; i<2000; i++) {
            if(MemInfo[i].Addr != NULL) {
                printf("index %d, addr %p, size %d, func: %s\n", i, MemInfo[i].Addr, MemInfo[i].Size, MemInfo[i].Func);
            }
        }
    }
    printf("\n===============\n");
}
#endif
